"""
在线客服系统路由
包含用户聊天和客服管理功能的API和WebSocket接口

系统架构说明：
1. 使用WebSocket实现实时双向通信
2. 区分用户和客服两种角色
3. 支持消息广播和定向发送
4. 提供连接管理、心跳检测等功能
5. 实现历史消息加载机制
"""

# 导入标准库
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Cookie, Request, Response
from typing import Optional, Dict
import uuid
import json
from datetime import datetime

# 导入项目内部模块
from tools.customer_service import (
    userdict, servicedict, serviceid,
    broadcast, send_to_client, get_room_id, is_service_client
)

# 创建路由
# APIRouter用于组织和注册相关路由端点
router = APIRouter()

# 存储消息历史（实际项目中应该使用数据库）
# 消息历史数据结构：{room_id: [messages]}
# 在生产环境中应使用数据库存储，此处为演示目的使用内存存储
message_history: Dict[str, list] = {}  # {room_id: [messages]}


@router.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: str):
    """
    WebSocket 连接端点
    自动判断是用户还是客服
    
    WebSocket连接处理流程：
    1. 接受WebSocket连接
    2. 根据客户端ID判断角色（用户或客服）
    3. 将连接添加到对应的角色字典中
    4. 发送欢迎消息和初始化信息
    5. 对于用户，加载历史消息
    6. 通知客服有新用户连接
    7. 进入消息接收循环
    8. 处理不同类型的消息（聊天、心跳等）
    9. 处理连接断开，清理资源
    
    参数:
        websocket: WebSocket 连接对象
              FastAPI提供的WebSocket连接实例，用于双向通信
        client_id: 客户端ID（用户ID或客服ID）
              用于标识连接的客户端身份
    """
    # 接受WebSocket连接请求
    # accept方法完成WebSocket握手协议，建立连接
    await websocket.accept()
    
    # 判断是客服还是用户
    # 通过is_service_client函数根据ID判断客户端角色
    is_service = is_service_client(client_id)
    
    # 根据客户端角色进行不同处理
    if is_service:
        # 客服连接处理逻辑
        # 将客服WebSocket连接存储到客服字典中
        servicedict[client_id] = websocket
        # 记录客服连接日志
        print(f"[客服] 客服 {client_id} 已连接")
        
        # 发送欢迎消息和在线用户列表
        # 通过send_json方法向客服发送连接确认和当前在线用户信息
        await websocket.send_json({
            "type": "connection",           # 消息类型：连接确认
            "role": "service",              # 角色：客服
            "message": "客服连接成功",        # 消息内容
            "client_id": client_id,         # 客户端ID
            "online_users": list(userdict.keys())  # 当前在线用户列表
        })
        
    else:
        # 用户连接处理逻辑
        # 将用户WebSocket连接存储到用户字典中
        userdict[client_id] = websocket
        # 记录用户连接日志
        print(f"[用户] 用户 {client_id} 已连接")
        
        # 发送欢迎消息
        # 通过send_json方法向用户发送连接确认信息
        await websocket.send_json({
            "type": "connection",           # 消息类型：连接确认
            "role": "user",                 # 角色：用户
            "message": "连接成功",            # 消息内容
            "client_id": client_id,         # 客户端ID
            "room_id": get_room_id(client_id)  # 用户所属房间ID
        })
        
        # 加载历史消息
        # 为用户提供最近的聊天记录，提升用户体验
        room_id = get_room_id(client_id)
        """
        函数中构造了history_messages列表，并通过send_json方法将其作为历史消息数据发送出去。
        """
        # 检查房间是否有历史消息
        if room_id in message_history:
            # 获取最近50条历史消息
            history_messages = message_history[room_id][-50:]  # 最近50条
            # 向用户发送历史消息
            await websocket.send_json({
                "type": "history",              # 消息类型：历史消息
                "messages": history_messages    # 历史消息列表
            })
        
        # 通知客服有新用户连接
        # 通过send_to_client方法向客服发送用户上线通知
        await send_to_client(serviceid, {
            "type": "user_online",              # 消息类型：用户上线
            "user_id": client_id,               # 上线用户ID
            "message": f"用户 {client_id} 已连接",  # 消息内容
            "online_users": list(userdict.keys())  # 更新后的在线用户列表
        }, is_service=True)
    
    try:
        # 保持连接，接收消息
        # 持续监听客户端发送的消息
        while True:
            try:
                # 接收客户端消息
                # receive_text方法等待并接收客户端发送的文本消息
                data = await websocket.receive_text()
                # 将JSON字符串解析为Python字典
                message_data = json.loads(data)
                
                # 获取消息类型
                message_type = message_data.get("type")
                
                # 根据消息类型进行不同处理
                if message_type == "chat":
                    # 聊天消息处理
                    content = message_data.get("content", "")           # 消息内容
                    sender_id = message_data.get("sender_id", client_id)  # 发送者ID
                    timestamp = message_data.get("timestamp", datetime.now().isoformat())  # 时间戳
                    
                    # 根据发送者角色进行不同处理
                    if is_service:
                        # 客服发送消息处理逻辑
                        target_user_id = message_data.get("target_user_id")
                        # 确保有目标用户
                        if target_user_id:
                            room_id = get_room_id(target_user_id)
                            
                            # 保存消息到历史记录
                            # 检查房间是否存在，不存在则创建
                            if room_id not in message_history:
                                message_history[room_id] = []
                            
                            # 构造消息对象
                            message = {
                                "sender_id": sender_id,         # 发送者ID
                                "sender_role": "service",       # 发送者角色
                                "content": content,             # 消息内容
                                "timestamp": timestamp          # 时间戳
                            }
                            # 将消息添加到房间历史记录中
                            message_history[room_id].append(message)
                            
                            # 发送给目标用户
                            # 通过send_to_client方法将消息发送给指定用户
                            await send_to_client(target_user_id, {
                                "type": "message",              # 消息类型：聊天消息
                                "sender_id": sender_id,         # 发送者ID
                                "sender_role": "service",       # 发送者角色
                                "content": content,             # 消息内容
                                "room_id": room_id,             # 房间ID
                                "timestamp": timestamp          # 时间戳
                            }, is_service=False)
                            
                            # 同时发送给自己（确认发送成功）
                            # 向发送者发送消息发送确认
                            await websocket.send_json({
                                "type": "message_sent",         # 消息类型：发送确认
                                "content": content,             # 消息内容
                                "target_user_id": target_user_id,  # 目标用户ID
                                "timestamp": timestamp          # 时间戳
                            })
                    else:
                        # 用户发送消息处理逻辑
                        room_id = get_room_id(client_id)
                        
                        # 保存消息到历史记录
                        # 检查房间是否存在，不存在则创建
                        if room_id not in message_history:
                            message_history[room_id] = []
                        
                        # 构造消息对象
                        message = {
                            "sender_id": sender_id,         # 发送者ID
                            "sender_role": "user",          # 发送者角色
                            "content": content,             # 消息内容
                            "timestamp": timestamp          # 时间戳
                        }
                        # 将消息添加到房间历史记录中
                        message_history[room_id].append(message)
                        
                        # 发送给客服
                        # 通过send_to_client方法将消息发送给客服
                        await send_to_client(serviceid, {
                            "type": "message",              # 消息类型：聊天消息
                            "sender_id": sender_id,         # 发送者ID
                            "sender_role": "user",          # 发送者角色
                            "content": content,             # 消息内容
                            "room_id": room_id,             # 房间ID
                            "user_id": client_id,           # 用户ID
                            "timestamp": timestamp          # 时间戳
                        }, is_service=True)
                        
                        # 同时发送给自己（确认发送成功）
                        # 向发送者发送消息发送确认
                        await websocket.send_json({
                            "type": "message_sent",         # 消息类型：发送确认
                            "content": content,             # 消息内容
                            "timestamp": timestamp          # 时间戳
                        })
                
                elif message_type == "ping":
                    # 心跳检测处理
                    # 回复pong消息确认连接正常
                    await websocket.send_json({
                        "type": "pong",                     # 消息类型：心跳响应
                        "message": "连接正常"                 # 消息内容
                    })
                    
            except WebSocketDisconnect:
                # 处理WebSocket连接断开异常
                # 客户端主动断开连接时会触发此异常
                break
            except Exception as e:
                # 处理其他消息处理异常
                # 记录错误日志并打印堆栈信息便于调试
                print(f"[WebSocket] 处理消息错误: {e}")
                import traceback
                traceback.print_exc()
                
    except WebSocketDisconnect:
        # 处理主循环中的WebSocket连接断开异常
        # 连接断开时跳出消息接收循环
        pass
    except Exception as e:
        # 处理其他连接异常
        # 记录连接错误日志
        print(f"[WebSocket] 连接错误: {e}")
    finally:
        # 清理连接资源
        # 无论正常断开还是异常断开都会执行清理逻辑
        if is_service:
            # 客服连接清理
            # 从客服字典中移除连接并通知所有客户端
            servicedict.pop(client_id, None)
            print(f"[客服] 客服 {client_id} 已断开")
            # 广播客服下线消息
            await broadcast({
                "type": "service_offline",          # 消息类型：客服下线
                "service_id": client_id,            # 客服ID
                "message": "客服已下线"              # 消息内容
            })
        else:
            # 用户连接清理
            # 从用户字典中移除连接并通知客服
            userdict.pop(client_id, None)
            print(f"[用户] 用户 {client_id} 已断开")
            # 通知客服用户已断开连接
            await send_to_client(serviceid, {
                "type": "user_offline",             # 消息类型：用户下线
                "user_id": client_id,               # 用户ID
                "message": f"用户 {client_id} 已断开",  # 消息内容
                "online_users": list(userdict.keys())  # 更新后的在线用户列表
            }, is_service=True)


@router.get("/api/customer-service/online-users")
async def get_online_users():
    """
    获取在线用户列表（供客服端使用）
    
    功能说明：
    1. 返回当前所有在线用户的ID列表
    2. 提供用户总数信息
    3. 供客服端展示在线用户状态
    """
    # 构造并返回在线用户信息
    return {
        "success": True,                    # 操作功标识
        "online_users": list(userdict.keys()),  # 在线用户ID列表
        "count": len(userdict)              # 在线用户总数
    }


@router.get("/api/customer-service/user-id")
async def get_or_create_user_id(response: Response, user_id: Optional[str] = Cookie(None)):
    """
    获取或创建用户ID
    如果Cookie中没有user_id，生成一个新的UUID并设置到Cookie中
    
    用户ID管理机制：
    1. 检查请求Cookie中是否包含用户ID
    2. 如果存在，直接返回该ID
    3. 如果不存在，生成新的UUID作为用户ID
    4. 将新ID设置到响应Cookie中，30天有效期
    5. 返回用户ID给客户端
    
    参数:
        response: FastAPI响应对象，用于设置Cookie
        user_id: 从Cookie中获取的用户ID（可选）
    """
    # 检查Cookie中是否包含用户ID
    if not user_id:
        # 生成新的UUID作为用户ID
        user_id = str(uuid.uuid4())
        # 设置Cookie，30天有效期
        response.set_cookie(
            key="user_id",                  # Cookie键名
            value=user_id,                  # Cookie值
            max_age=86400 * 30,             # 有效期30天（秒）
            httponly=False,                 # 允许前端JavaScript访问
            samesite="lax"                  # SameSite策略，防止CSRF攻击
        )
    
    # 返回用户ID信息
    return {
        "success": True,                    # 操作成功标识
        "user_id": user_id                  # 用户ID
    }


@router.get("/api/customer-service/messages/{user_id}")
async def get_user_messages(user_id: str):
    """
    获取用户的历史消息
    
    消息查询机制：
    1. 根据用户ID生成房间ID
    2. 查询该房间的历史消息
    3. 返回消息列表和总数
    
    参数:
        user_id: 用户ID
              用于定位用户所属房间和历史消息
    """
    # 根据用户ID生成房间ID
    room_id = get_room_id(user_id)
    
    # 查询房间历史消息
    if room_id in message_history:
        # 房间存在历史消息
        messages = message_history[room_id]
    else:
        # 房间无历史消息
        messages = []
    
    # 返回历史消息信息
    return {
        "success": True,                    # 操作成功标识
        "room_id": room_id,                 # 房间ID
        "messages": messages,               # 消息列表
        "count": len(messages)              # 消息总数
    }